from dataclasses import dataclass, field
from typing import List, Dict, Tuple
from itertools import chain


@dataclass(frozen=True)
class Pos:
    line: int
    column: int

    @property
    def tuple(self) -> Tuple[int, int]:
        return (self.line, self.column)

    def __gt__(self, obj: 'Pos') -> bool:
        return self.tuple > obj.tuple

    def __ge__(self, obj: 'Pos') -> bool:
        return self.tuple >= obj.tuple

    def __lt__(self, obj: 'Pos') -> bool:
        return self.tuple < obj.tuple

    def __le__(self, obj: 'Pos') -> bool:
        return self.tuple <= obj.tuple


@dataclass(frozen=True)
class PosRange:
    start: Pos
    end: Pos


@dataclass(frozen=True)
class FileChunk:
    filename: str
    start_pos: Pos
    end_pos: Pos

    def intersects(self, fc: 'FileChunk') -> bool:
        """ Returns whether a nother file chunk has intersection part. """
        if self.filename != fc.filename:
            return False
        elif self.start_pos > fc.end_pos or self.end_pos < fc.start_pos:
            return False

        return True

    def __add__(self, fc: 'FileChunk') -> 'FileChunk':
        assert self.filename == fc.filename
        return FileChunk(self.filename, min(self.start_pos, fc.start_pos), max(self.end_pos, fc.end_pos))

    def __repr__(self) -> str:
        return f"<file {self.filename}: [{self.start_pos}, {self.end_pos}]>"


@dataclass
class FileChunks:
    chunks: Dict[str, List[FileChunk]] = field(default_factory=dict)

    def add(self, fc: FileChunk) -> None:
        if fc.filename not in self.chunks:
            self.chunks[fc.filename] = []

        chunks = self.chunks[fc.filename]
        for i, existing_fc in enumerate(chunks):
            if existing_fc.intersects(fc):
                chunks[i] = existing_fc + fc
                break
        else:
            chunks.append(fc)

    def update(self, fcs: 'FileChunks') -> None:
        for fc in chain(*fcs.chunks.values()):
            self.add(fc)

    @classmethod
    def __summary_file(cls, file: str, chunks: List[FileChunk]) -> str:
        summary: List[str] = []
        with open(file, 'r') as f:
            lines = f.readlines()

        for chunk in chunks:
            summary += ["\n"]
            summary += lines[chunk.start_pos.line:chunk.end_pos.line + 1]

        return "".join(summary)

    def summary(self) -> str:
        summary = ""
        for filename, chunks in self.chunks.items():
            chunks.sort(key=lambda fc: fc.start_pos.line)
            summary += f"Here is part of the content of file <{filename}>:\n\n"
            summary += "```\n"
            summary += self.__summary_file(filename, chunks) + "\n"
            summary += "```\n"

        return summary


class CodeBase:

    def clear(self) -> None:
        raise NotImplementedError

    def load_directory(self, directory: str) -> None:
        raise NotImplementedError
